#include "ui_text.h"
#include "ui_widget.h"
#include "ui_manager.h"
#include "graphics/graphics.h"
#include "graphics/graphics_util.h"
#include "audio/audio.h"
#include "ui_replacement.h"

#include "console/console.h"

#include "input/keyboard.h"

#include "lang/strman.h"
#include "lang/lang.h"
#include "lang/swear.h"

#include "font/font.h"
#include "font/font_manager.h"



#include "util/timer.h"		    // Needs to come in early
#include "util/mmgr.h"		    // Needs to come in early
#include "util/common_macros.h"		// macros
#include "util/log.h"		    // logging

#include <algorithm>


using namespace ui;
bool UIText::s_ForceLeft = false;
bool UIText::s_InsertionCursor = true;


UIText::UIText() :
	m_pFont(0),
	m_Color(0xFFFFFFFF),
	m_VertJust(TJV_CENTER),
	m_HorizJust(TJH_CENTER),
	m_Border(0.005f, 0.005f),
	m_Flags(Font::FONT_WORDBREAK),
	m_CanEdit(false),
	m_CanEditMultiline(false),
	m_SwearFilter(false),
	m_BlinkOn(false),
	m_BlinkTime(0.0f),
	m_CursorFrom(0), m_CursorTo(0),
	m_CursorPrevFrom(-1), m_CursorPrevTo(-1),
	m_MaxInputLen(DEFAULT_MAX_INPUT_LEN),
	m_Blink(NULL),
	m_MultiLine(false),
	m_Clip(false),
	m_HasUnderline(false),
	m_IsPassword(false),
	m_ValidCharCB(NULL)
{
}

UIText::~UIText() 
{
	UTIL_SAFE_DELETE(m_Blink);
}

void	UIText::TrimSpaces()
{
	// truncate any multi-space to single space
	std::wstring	Result;
	unsigned int	Count = m_Text.size();
	unsigned int	Index = 0;
	unsigned short	A ;
	unsigned short	B ;

	if (m_Text.empty()) return;	// cannot ever trim
	B = A = m_Text[0];

	while (Index < Count)
	{	
		if (A == B && A == L' ')	// both space
		{
			
		} else
		{
				Result+=(wchar_t)(B);
		}	
		A = m_Text[Index];	
		B = m_Text[Index+1];	
		Index++;
	}
	if (Result[Result.size()-1]==L' ') Result.resize(Result.size()-1);
	m_Text = Result;
}

bool	UIText::Render()
{
	if (m_Text.empty() && !m_CanEdit) return false;	// early out.
	if (!m_pFont) return false;
	if (!Tick()) return false;

	unsigned int	Flag = Font::FONT_WORDBREAK | Font::FONT_EXPANDTABS;

//  This would enable forced left-align for multi-line east-asian languages
//	if (s_ForceLeft && m_MultiLine)		
//	{
//		// force left
//		Flag |= Font::FONT_LEFT;
//	} else
	{
		if (m_HorizJust == TJH_LEFT)	Flag |= Font::FONT_LEFT; else
		if (m_HorizJust == TJH_CENTER)	Flag |= Font::FONT_CENTER; else
		if (m_HorizJust == TJH_RIGHT)	Flag |= Font::FONT_RIGHT;
	}

	if (m_VertJust == TJV_TOP)		Flag |= Font::FONT_TOP; else
	if (m_VertJust == TJV_CENTER)	Flag |= Font::FONT_VCENTER; else
	if (m_VertJust == TJV_BOTTOM)	Flag |= Font::FONT_BOTTOM;
	
	if (!m_Text.empty())
	{
		m_pFont->SetUnderline(m_HasUnderline);
//		m_pFont->Render(m_Text, m_TopLeft+m_Border, m_BoxExtents-math::Vec2((m_Border.x),0), Flag, m_Color, m_Clip);
		if (m_IsPassword)
		{
			std::wstring	PWD;
			PWD.reserve(m_Text.size());
			for (int i=0; i<(int)m_Text.size(); i++)
			{
				PWD.push_back(L'X');
			}
			m_pFont->Render(PWD, m_TopLeft+m_Border, m_BoxExtents, Flag, m_Color, m_Clip);
		} else
		{
			m_pFont->Render(m_Text, m_TopLeft+m_Border, m_BoxExtents, Flag, m_Color, m_Clip);
		}
		m_pFont->FlushSprite();
		m_pFont->SetUnderline(false);	// restore
	}

	if (m_CanEdit && ui::UIManager::Instance().GetEditText() == this)
	{
	/*	m_BlinkTime	+= TIMER().Delta();
		if (m_BlinkTime > 0.0f) 
		{
			m_BlinkOn = !m_BlinkOn;
			m_BlinkTime = 0.0f;
		}
		if (m_BlinkOn) // && UIManager::Instance().GetFocus() == m_pOwner)
	*/
		{
			if (!m_Blink)
			{
				m_Blink = new UISprite();
				m_Blink->Create("ui_img/cursor.png");
			}
			m_Blink->Setup(m_TopLeft+m_Border+m_CursorPos, m_CursorSize);
			m_Blink->Render();
//			FontManager::Instance().Print(math::Vec2(0, 0.8f), "Cursor : %i->%i", 	m_CursorFrom, m_CursorTo);
		}
	}

	return true;
}


void	UIText::RefreshText()
{
	// force the font to reset
	SetFont(m_Font);

	// always look for a translated string first
	std::wstring SM;
	if (!m_CanEdit)	// only do text-replace on things you cannot edit
	{
		SM = lang::StrMan::Instance().GetStringW(m_OriginalText);
	}
	if (SM.empty())
	{
		m_Text = m_OriginalText;
	} else
	{
		m_Text = SM;
	}

	// do a text-replacement for any words used as keys
	UIReplacement::Instance().ReplaceAll(m_Text);

	if (!m_Text.empty())
	{
		if (m_pFont)
		{
//			m_pFont->BreakText(Text, m_Text, m_BoxExtents);
			math::Vec2	From;
			m_pFont->GetTextExtent(m_Text, m_BoxExtents, From, m_Size);
			m_MultiLine = m_Size.y > m_pFont->GetHeight()*1.9f;

		}
	}
	CalcExtents();
}

void	UIText::RecalcCursor()
{
	if (!m_pFont) return;
	if (m_CanEdit && (m_CursorPrevFrom != m_CursorFrom) || (m_CursorPrevTo != m_CursorTo))
	{
		/* ADRIAN: okay, let's try redoing this confusing math

		std::wstring	FromStr = m_Text.substr(0, m_CursorFrom);
		std::wstring	ToStr   = m_Text.substr(0, m_CursorTo);
		std::wstring	TextStr = m_Text;

		if (m_IsPassword == true)
		{
			std::wstring	PWD;
			PWD.reserve(m_Text.size());
			for (int i=0; i<(int)m_Text.size(); i++)
			{
				PWD.push_back(L'X');
			}

			FromStr = PWD.substr(0, m_CursorFrom);
			ToStr   = PWD.substr(0, m_CursorTo);
			TextStr = PWD;
		}

		if (FromStr.empty())
			FromStr = ' ';
		if (ToStr.empty()) 
			ToStr = ' ';
		if (FromStr.size() == ToStr.size())
		{
			if (m_CursorTo < (int)m_Text.size())
			{
				if (m_Text[m_CursorTo] != ' ')
					ToStr = m_Text.substr(0, m_CursorTo+1);
				else
					ToStr+= 'i';	// replace the ' ', which gives size 0
			}
			else				
				ToStr+= 'X';
		}

		if (m_Text.empty())
		{
			FromStr = ' '; 			
			ToStr = 'X';
			TextStr = ' ';
		}

		unsigned int	Flag = Font::FONT_WORDBREAK | Font::FONT_EXPANDTABS;
		if (m_HorizJust == TJH_CENTER)	Flag |= Font::FONT_CENTER; else
		if (m_HorizJust == TJH_RIGHT)	Flag |= Font::FONT_RIGHT;
		if (m_VertJust == TJV_CENTER)	Flag |= Font::FONT_VCENTER; else
		if (m_VertJust == TJV_BOTTOM)	Flag |= Font::FONT_BOTTOM;


		math::Vec2	From1,	FromSize;
		math::Vec2	From2,	ToSize;
		math::Vec2	From3,	TextSize;

		m_pFont->GetTextExtent(FromStr, m_BoxExtents-math::Vec2((m_Border.x*2),0), From1,	FromSize,	Font::FONT_WORDBREAK | Font::FONT_EXPANDTABS);
		m_pFont->GetTextExtent(ToStr,	m_BoxExtents-math::Vec2((m_Border.x*2),0), From2,	ToSize,		Font::FONT_WORDBREAK | Font::FONT_EXPANDTABS);
		m_pFont->GetTextExtent(TextStr,	m_BoxExtents-math::Vec2((m_Border.x*2),0), From3,	TextSize,	Flag);

		if (FromStr.size() <= 1)
		{
			// fudge the fact that ! is used to calculate the size of empty strings
			math::Vec2	EmptyPos, EmptySize;
			m_pFont->GetTextExtent(" ", m_BoxExtents, EmptyPos,	EmptySize,	Font::FONT_WORDBREAK | Font::FONT_EXPANDTABS);
			From3.x -= EmptySize.x;
			ToSize.x += (EmptySize.x);
		}

		m_CursorSize.x = (ToSize.x - From2.x) - (FromSize.x - From1.x);
		m_CursorSize.y = ToSize.y - From2.y;

		m_CursorPos.x = From3.x + FromSize.x + m_Border.x; // ADRIAN: add border because above calculations did not account for that position shift
		m_CursorPos.y = From3.y;
		*/

		const float INSERTION_CURSOR_WIDTH = 2.0f * GraphicsUtil::InvW;
		bool UseInsertionCursor = false;

		std::wstring FromStr, ToStr, TextStr;
		math::Vec2 From1, From2, From3, FromSize, ToSize, TextSize;
		math::Vec2 CharOffset(0.0f, 0.0f);

		// clamp cursor positions to avoid unexpected crap
		UTIL_CLAMP(m_CursorFrom, 0, int(m_Text.size()));
		UTIL_CLAMP(m_CursorTo, m_CursorFrom, int(m_Text.size()));

		// check password first
		if (m_IsPassword)
		{
			TextStr.reserve(m_Text.size());
			for (int i=0; i<(int)m_Text.size(); i++)
				TextStr.push_back(L'X');
		}
		else
			TextStr = m_Text;

		// determine extents based on cursor position
		if (m_Text.empty())
		{
			// empty text
			FromStr = L"";
			ToStr = L" ";
			TextStr = L" ";
			UseInsertionCursor = s_InsertionCursor;
		}
		else if (m_CursorFrom >= (int)m_Text.size())
		{
			// cursor is at end of text
			FromStr = TextStr;
			ToStr = FromStr + L" ";
			UseInsertionCursor = s_InsertionCursor;
		}
		else if (m_CursorFrom == m_CursorTo)
		{
			// single selection
			FromStr = (0 == m_CursorFrom) ? L"" : TextStr.substr(0, m_CursorFrom);
			ToStr = ((m_CursorTo+1) < (int)m_Text.size()) ? TextStr.substr(0, m_CursorTo+1) : TextStr;
			UseInsertionCursor = s_InsertionCursor;
		}
		else
		{
			// std selection
			FromStr = (0 == m_CursorFrom) ? L"" : TextStr.substr(0, m_CursorFrom);
			ToStr = (m_CursorTo < (int)m_Text.size()) ? TextStr.substr(0, m_CursorTo) : TextStr;
		}

		if ((m_CursorFrom > 0) && (m_CursorFrom < (int)m_Text.size()))
		{
			// correct/adjust offset based on individual character spacing
			int FontOffset;
			int PreOffset;
			if ( m_pFont->GetCharOffsets(m_Text[m_CursorFrom-1], NULL, &PreOffset) &&
				 m_pFont->GetCharOffsets(m_Text[m_CursorFrom], &FontOffset, NULL) )
			{
				CharOffset.x = 0.5f * float(FontOffset - PreOffset) * GraphicsUtil::InvW;
			}
		}

		unsigned int	Flag = Font::FONT_WORDBREAK | Font::FONT_EXPANDTABS;
		if (m_HorizJust == TJH_CENTER)	Flag |= Font::FONT_CENTER; else
		if (m_HorizJust == TJH_RIGHT)	Flag |= Font::FONT_RIGHT;
		if (m_VertJust == TJV_CENTER)	Flag |= Font::FONT_VCENTER; else
		if (m_VertJust == TJV_BOTTOM)	Flag |= Font::FONT_BOTTOM;

		m_pFont->GetTextExtent(ToStr,	m_BoxExtents-math::Vec2((m_Border.x*2),0), From2,	ToSize,		Font::FONT_WORDBREAK | Font::FONT_EXPANDTABS);
		m_pFont->GetTextExtent(TextStr,	m_BoxExtents-math::Vec2((m_Border.x*2),0), From3,	TextSize,	Flag);

		if (FromStr.empty())
		{
			From1 = From3;
			FromSize = math::Vec2(0.0f, TextSize.y);
		}
		else
			m_pFont->GetTextExtent(FromStr, m_BoxExtents-math::Vec2((m_Border.x*2),0), From1,	FromSize,	Font::FONT_WORDBREAK | Font::FONT_EXPANDTABS);

		if (UseInsertionCursor)
		{
			m_CursorSize.x = INSERTION_CURSOR_WIDTH;
			m_CursorSize.y = ToSize.y - From2.y;
			m_CursorPos.x = CharOffset.x + From3.x + FromSize.x + m_Border.x - (0.5f * INSERTION_CURSOR_WIDTH);
			m_CursorPos.y = CharOffset.y + From3.y;
		}
		else
		{
			m_CursorSize.x = ToSize.x - FromSize.x;
			m_CursorSize.y = ToSize.y - From2.y;
			m_CursorPos.x = CharOffset.x + From3.x + FromSize.x + m_Border.x;
			m_CursorPos.y = CharOffset.y + From3.y;
		}

		m_CursorPrevFrom = m_CursorFrom;
		m_CursorPrevTo = m_CursorTo;
	}
}

int		UIText::GetIndexFromPos(const math::Vec2& Pos)
{
	unsigned int	Flag = Font::FONT_WORDBREAK | Font::FONT_EXPANDTABS;
	if (m_HorizJust == TJH_CENTER)	Flag |= Font::FONT_CENTER; else
	if (m_HorizJust == TJH_RIGHT)	Flag |= Font::FONT_RIGHT;
	if (m_VertJust == TJV_CENTER)	Flag |= Font::FONT_VCENTER; else
	if (m_VertJust == TJV_BOTTOM)	Flag |= Font::FONT_BOTTOM;

	math::Vec2	From3,	TextSize;
	m_pFont->GetTextExtent(m_Text,	m_BoxExtents, From3,	TextSize,	Flag);
	if (Pos.x < m_BoxExtents.x + From3.x ) return 1;
	if (Pos.x > m_BoxExtents.x + From3.x + TextSize.x) return m_Text.size()+1;

	for (int i=0; i<(int)m_Text.size(); i++)
	{
		std::wstring	Sub = m_Text.substr(0, i);
		math::Vec2		CharPos, CharFrom;
		m_pFont->GetTextExtent(Sub, m_BoxExtents, CharFrom, CharPos, 0);

		if (m_TopLeft.x+m_Border.x+From3.x +CharPos.x > Pos.x) 
			return i;
	}
	return (int)m_Text.size()+1;
}

void	UIText::AppendText(const std::wstring& Text)
{
	const std::wstring& SM = lang::StrMan::Instance().GetStringW(Text);
	if (!SM.empty())
	{
		m_Text = m_Text + SM;
	} else
	{
		m_Text = m_Text + Text;
	}
	SetText(m_Text);
}

void	UIText::SetText(const std::wstring& Text)
{
	// recalc size on the fly ?
	// add support for different centering modes
	// add support for SFX ?

	if (m_SwearFilter)
	{
		lang::Swear::Instance().SanitizeW(Text, m_OriginalText);	
	}	else
	{
		m_OriginalText = Text;
	}


	RefreshText();

	
	if (m_CanEdit)
	{
		m_CursorFrom = m_Text.size()+1;
		m_CursorTo = m_CursorFrom;

		RecalcCursor();
	}
}

void	UIText::SelectRegion(const math::Vec2& From, const math::Vec2& To)
{
	m_CursorFrom = GetIndexFromPos(From)-1;
	m_CursorTo =	GetIndexFromPos(To);



	if (m_CursorFrom > m_CursorTo) 
	{
		int Temp = m_CursorTo;
		m_CursorTo = m_CursorFrom+1;
		m_CursorFrom = Temp-1;
	}
	if (m_CursorFrom < 0) m_CursorTo  = 0;
	if (m_CursorTo >= (int)m_Text.size()) m_CursorTo = (int)m_Text.size();
	RecalcCursor();
}

void	UIText::SelectAll()
{
	m_CursorFrom = 0;
	m_CursorTo = m_Text.size();
	RecalcCursor();
}

void	UIText::PutCursor(const math::Vec2& Pos)
{
	m_CursorTo	= m_CursorFrom = GetIndexFromPos(Pos)-1;
	RecalcCursor();
}


void	UIText::SetFont(const std::string& Font)
{
	m_Font = Font;
	m_pFont = FontManager::Instance().GetFont(Font);
	if (!m_pFont)
		m_pFont = FontManager::Instance().GetDefaultFont();
}

Font*		UIText::GetFont() const
{
	return m_pFont;
}

const std::string& UIText::GetFontName() const
{
	return m_Font;

}


void	UIText::SetPos(const math::Vec2& Pos)
{
	m_Pos = Pos;
}

bool	UIText::IsInside(const math::Vec2& Point)
{
	if (m_pOwner)
	{
		return m_pOwner->IsInside(Point);
	} else
	{
		return (UTIL_RANGE(Point.x, m_Pos.x, m_Pos.x + m_Size.x) && UTIL_RANGE(Point.y, m_Pos.y, m_Pos.y + m_Size.y));
	}
}		

bool UIText::Tick()
{
	return true;
}

void UIText::CalcExtents()
{
	switch (m_VertJust)
	{
		case TJV_CENTER:
			m_Pos.y = m_TopLeft.y + ((m_BoxExtents.y - m_Size.y) * 0.5f);
			break;
		case TJV_BOTTOM:
			m_Pos.y = m_TopLeft.y + (m_BoxExtents.y - m_Size.y) - (m_Border.y);
			break;
		case TJV_TOP:
		default:
			m_Pos.y = m_TopLeft.y + m_Border.y;
	}

	switch (m_HorizJust)
	{
		case TJH_CENTER:
			m_Pos.x = m_TopLeft.x + ((m_BoxExtents.x - m_Size.x) * 0.5f);
			break;
		case TJH_RIGHT:
			m_Pos.x = m_TopLeft.x + (m_BoxExtents.x - m_Size.x) - (m_Border.x);
			break;
		case TJH_LEFT:
		default:
			m_Pos.x = m_TopLeft.x + m_Border.x;
	}
}

void UIText::SetExtents(const math::Vec2& TopLeft, const math::Vec2& BoxExtents)
{
	m_TopLeft		=	TopLeft;
    m_BoxExtents	=	BoxExtents - (m_Border*2);

	// reset cache after m_BoxExtents has changed
	m_CursorPrevFrom = m_CursorPrevTo = -1;

	CalcExtents();
}

void UIText::SetVertJust(const std::string& Text)
{
	/*
	std::string	lower = Text;
    std::transform(lower.begin(), lower.end(), lower.begin(), tolower);

	if (lower == "top")
		SetVertJust(TJV_TOP);
	else
	if (lower == "bottom")
		SetVertJust(TJV_BOTTOM);
	else
		SetVertJust(TJV_CENTER);
	*/
	SetVertJust( MapStringToVertJust(Text) );
}

void UIText::SetHorizJust(const std::string& Text)
{
	/*
	std::string	lower = Text;
    std::transform(lower.begin(), lower.end(), lower.begin(), tolower);

	if (lower == "left")
		SetHorizJust(TJH_LEFT);
	else
	if (lower == "right")
		SetHorizJust(TJH_RIGHT);
	else
		SetHorizJust(TJH_CENTER);
	*/
	SetHorizJust( MapStringToHorizJust(Text) );
}

std::string		UIText::GetHorizJustText() const
{
	/*
	switch (m_HorizJust)
	{
		case TJH_CENTER: return "center";
		case TJH_LEFT: return "left";
		case TJH_RIGHT: return "right";
	}
	return "center";
	*/
	return MapHorizJustToString(m_HorizJust);
}

std::string		UIText::GetVertJustText() const
{
	/*
	switch (m_VertJust)
	{
		case TJV_CENTER: return "center";
		case TJV_TOP:	return "top";
		case TJV_BOTTOM: return "bottom";
	}
	return "center";
	*/
	return MapVertJustToString(m_VertJust);
}


void UIText::SetCanEdit(bool b)
{
	m_CanEdit = b;
	m_SwearFilter = b;	// by default, follow the editable
}

void UIText::SetCanEditMultiline(bool b)
{
	if (m_CanEditMultiline = b)
		SetCanEdit(true);
}

bool	UIText::CapText()
{
	bool	Capped = false;
	math::Vec2 From;
	math::Vec2 Size;
	int LineC = 0;
	m_pFont->GetTextExtent(m_Text, m_BoxExtents, From, Size, Font::FONT_WORDBREAK, &LineC);

	// ADRIAN: cap both width and height
	// ALLAN: Should be X > Size || (!MultiLine && Y > Size), not X > Size && (!MultiLine || Y > Size)
	/* ADRIAN: no, that broke a bunch of stuff

	while ( (Size.x > (0.97f * m_BoxExtents.x - (2.0f*m_Border.x))) ||
			((Size.y > (m_BoxExtents.y - m_Border.y)) && !m_CanEditMultiline) ) // allow more tolerance to height check
	{
		m_Text.resize(m_Text.size()-1);
		if (m_Text.empty()) return true;
		math::Vec2	From;
		m_pFont->GetTextExtent(m_Text, m_BoxExtents, From, Size);
		Capped = true;
		RecalcCursor();
	}
	*/

	// ADRIAN:fitting everything in one line is confusing, let's break it down
	while (!m_Text.empty())
	{
		if (m_CanEditMultiline)
		{
			// ----- assume any line width will automatically have a linebreak so we don't need to check width

			if (LineC == 1) break; // always allow minimum 1 line
			if (Size.y <= (m_BoxExtents.y - m_Border.y)) break; // yup, fits perfectly
		}
		else
		{
			// ----- only allow 1 line, and width must fit text area; ignore height to always allow minimum 1 line

			if ((LineC == 1) && (Size.x < (0.97f * m_BoxExtents.x - (2.0f*m_Border.x)))) break; // fits
		}

		// if we reach this point it means we need to cap text

		m_Text.resize(m_Text.size()-1);
		if (m_Text.empty()) return true;
		math::Vec2	From;
		m_pFont->GetTextExtent(m_Text, m_BoxExtents, From, Size, Font::FONT_WORDBREAK, &LineC);
		Capped = true;
		RecalcCursor();
	}

	return Capped;
}

static bool		TestIgnoreChar(unsigned short Key)
{
	const unsigned char IGNORE_CHAR[] = 
	{
		'!', '#', '$', '%', '^', '&', '*', '(', ')',  '[', ']', '<', '>', '\\', '{', '}', '|', '<', '>'
	};
	const int	IGNORE_COUNT = sizeof(IGNORE_CHAR) / sizeof(unsigned char);

/*	for (int i=0; i<IGNORE_COUNT; i++)
	{
		if (Key==IGNORE_CHAR[i]) return true;
	}*/
	return false;
}

void UIText::CharReceived(unsigned int Key)
{
	unsigned short Ch = Key & 0xFFFF;
	if (m_ValidCharCB && !m_ValidCharCB(Ch)) return;	// we didn't like this character
	// stop overruns
	if (m_CursorFrom!=m_CursorTo)
	{
		int					From = m_CursorFrom;
		std::wstring		Pre  = m_Text.substr(0, m_CursorFrom);
		std::wstring		Post = m_Text.substr(m_CursorTo, m_Text.size() - m_CursorTo);
		SetText(Pre+Post);
		m_CursorFrom = m_CursorTo = From;
		RecalcCursor();
	}
	if ((int)m_Text.size() < m_MaxInputLen)
	{
		int					From = m_CursorFrom;
		if (m_Text.empty())
		{
			m_Text += Ch;
			From = 1;
		} else
		if (m_CursorFrom >=(int) m_Text.size())
		{
			m_Text += Ch;
			From+=1;
		} else
		{
			m_Text.insert(m_Text.begin()+m_CursorFrom, Ch);
			From+=1;
		}
		//m_Text.push_back(Ch);
		SetText(m_Text);
		if (CapText())
		{
			m_CursorFrom = m_CursorTo = m_Text.size();
		} 
		else
		{
			m_CursorFrom = m_CursorTo = From;
		}
		RecalcCursor();
	}
}

void UIText::KeyPressed(input::Keyboard::KEY* pKey)
{
	if (TestIgnoreChar((unsigned short)pKey->KeyID) == true && m_IsPassword == false)
	{
		_LOG(MSG_COMMENT, "Tried typing an invalid character " << pKey->KeyID);
		return;
	}
	if (console::Console::Instance().IsActive()) return;	// annoying double-typing if the console is down

	input::Keyboard* pKB = &GET_KEYBOARD();
	bool	ShiftDown = pKB->IsShiftDown();
	bool	CtrlDown = pKB->IsCtrlDown();

	UTIL_CLAMP(m_CursorFrom, 0, 1024);
	UTIL_CLAMP(m_CursorTo, 0, 1024);

	if (pKey->KeyID != KEY_LSHIFT && 
		pKey->KeyID != KEY_RSHIFT && 
		pKey->KeyID != KEY_LCONTROL && 
		pKey->KeyID != KEY_RCONTROL)
	{
		AUDIO().PlayEvent("textbox_new_text");

	}

	// look for special keys:
	unsigned short	Ch = (unsigned short)pKey->KeyID;
	switch (pKey->KeyID)
	{
		case KEY_LEFT:
			if (m_CursorTo > m_CursorFrom)
			{
				// for a group selection, LEFT sets left-most selection
				m_CursorTo = m_CursorFrom;
				RecalcCursor();
			}
			else if (m_CursorFrom > 0)
			{
				m_CursorFrom--;
				m_CursorTo = m_CursorFrom;
				RecalcCursor();
			}
		break;
		case KEY_RIGHT:
			if (m_CursorTo > m_CursorFrom)
			{
				// for a group seleciton, RIGHT sets right-most selection
				m_CursorFrom = m_CursorTo;
				RecalcCursor();
			}
			else if (m_CursorFrom < (int)m_Text.size())
			{
				m_CursorFrom++;
				m_CursorTo = m_CursorFrom;
				RecalcCursor();
			}
		break;
		case KEY_HOME:
			m_CursorFrom = 0;
			m_CursorTo = m_CursorFrom;
			RecalcCursor();
		break;
		case KEY_END:
			m_CursorFrom = (int)m_Text.size();
			m_CursorTo = m_CursorFrom;
			RecalcCursor();
		break;
/*		case KEY_UP:
			if (m_PromptIndex>0)
			{
				m_PromptIndex--;
				m_Text = m_PromptList[m_PromptIndex];
			}
		break;
		case KEY_DOWN:
			{
				size_t	Count = m_PromptList.size();
				if (Count - m_PromptIndex > 1)
				{
					m_PromptIndex++;
					m_Text = m_PromptList[m_PromptIndex];
				}
			}
		break;
		case KEY_GRAVE:
			m_IsActive = false;
		break;

*/		case KEY_INSERT:
			{
				if (ShiftDown)
				{
					std::string		Buffer;
					if (pKB->GetClipBoard(Buffer))
					{
						m_Text = m_Text + lang::Lang::ConvertAtoW(Buffer);
					}
					SetText(m_Text);
					CapText();
				} else
				if (CtrlDown)
				{
					if (m_CursorFrom==m_CursorTo)
					{
						pKB->SetClipBoard(m_Text);
					} else
					{
						std::wstring		Clip = m_Text.substr(m_CursorFrom, m_CursorTo-m_CursorFrom);
						pKB->SetClipBoard(Clip);
					}
				}
			}
		break;
		case KEY_BACKSPACE:
/*			if (m_Text.size() > 0)
				m_Text.resize(m_Text.size()-1);
*/
			if (m_Text.size() > 0 && m_CursorFrom >= 0)
			{
				int					From = m_CursorFrom;
				if (m_CursorFrom <= 0) return;	
				if (m_CursorFrom==m_CursorTo )
				{
					int Size = m_Text.size();
					if (m_CursorFrom - 1 < Size )
						m_Text.erase(m_Text.begin() + m_CursorFrom - 1);
					else
						m_Text.resize(Size-1);
					SetText(m_Text);
				} else
				{
					int					From = m_CursorFrom;
					std::wstring		Pre  = m_Text.substr(0, m_CursorFrom);
					std::wstring		Post = m_Text.substr(m_CursorTo, m_Text.size() - m_CursorTo);
					SetText(Pre+Post);
				}
				m_CursorFrom = m_CursorTo = From-1;
				RecalcCursor();
			}
		break;
		case KEY_RETURN:
			if (m_pOwner && !m_pOwner->IsMoving())
			{
				m_pOwner->TriggerEvent(UIWidget::WE_TEXT_ENTER);
			}
		break;
		case KEY_DELETE:
			if (!m_Text.empty())
			{
				int					From = m_CursorFrom;
				std::wstring		Pre  = m_Text.substr(0, m_CursorFrom);
				std::wstring		Post = m_Text.substr(m_CursorTo, m_Text.size() - m_CursorTo);
				if (m_CursorTo+1 >= (int)m_Text.size())
					m_CursorTo = (int)(m_Text.size())-1;
				if (m_CursorFrom==m_CursorTo)		
					Post = m_Text.substr(m_CursorTo+1, m_Text.size() - m_CursorTo-1);

				SetText(Pre+Post);
				// restore cursor
				m_CursorFrom = m_CursorTo = From;
				RecalcCursor();
			}
			break;
	}
	RecalcCursor();
	//triggering new input event
	m_pOwner->TriggerEvent(UIWidget::WE_TEXT_NEWINPUT);
}



// static mapping functions

std::string UIText::MapHorizJustToString(TEXT_JUST_HORIZ t)
{
	switch (t)
	{
	case TJH_LEFT:		return "left";
	case TJH_RIGHT:		return "right";
	case TJH_CENTER:
	default:			return "center";
	}
}

UIText::TEXT_JUST_HORIZ UIText::MapStringToHorizJust(const std::string& t)
{
	for (TEXT_JUST_HORIZ i = (TEXT_JUST_HORIZ)0; i < TJH_COUNT; i = (TEXT_JUST_HORIZ)(i+1))
	{
		if (stricmp(t.c_str(), MapHorizJustToString(i).c_str()) == 0)
			return i;
	}
	return TJH_CENTER;
}

std::string UIText::MapVertJustToString(TEXT_JUST_VERT t)
{
	switch (t)
	{
	case TJV_TOP:		return "top";
	case TJV_BOTTOM:	return "bottom";
	case TJV_CENTER:
	default:			return "center";
	}
}

UIText::TEXT_JUST_VERT UIText::MapStringToVertJust(const std::string& t)
{
	for (TEXT_JUST_VERT i = (TEXT_JUST_VERT)0; i < TJV_COUNT; i = (TEXT_JUST_VERT)(i+1))
	{
		if (stricmp(t.c_str(), MapVertJustToString(i).c_str()) == 0)
			return i;
	}
	return TJV_CENTER;
}
